#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import time
import datetime
from xml.sax.handler import feature_string_interning

from farado.logger import logger
from farado.general_manager_holder import gm_holder
from farado.helpers.long_action_watcher import LongActionHandler
from farado.helpers.issue_statistics_helper import states_from_issue_change


class BoardStatisticsHelper(LongActionHandler):

    def __init__(self, board) -> None:
        self.board = board

        filter_key = self.board.filter_key()
        self.issues = gm_holder.project_manager.filtered_issues(
            kinds_ids=self.board.filter_by_key(filter_key.kinds_ids),
            projects_ids=self.board.filter_by_key(filter_key.projects_ids),
            versions_ids=self.board.filter_by_key(filter_key.versions_ids),
        )

        states = gm_holder.project_manager.states()
        self.active_states = [state.id for state in states if state.is_active()]
        self.finished_states = [state.id for state in states if state.is_finished()]

    def issues_by_state_id(self, state_id):
        return [issue for issue in self.issues if issue.state_id == state_id]

    def is_finished(self, issue):
        return issue.state_id in self.finished_states

    def is_active(self, issue):
        return issue.state_id in self.active_states

    #--------------------------------------------------------------------------#
    def cumulative_flow_diagram_data(self):
        '''Формирует данные для построения накопительной диаграммы потока'''

        states_ids, timestamps_states_issues = self.timestamps_states_issues_data()
        ordered_timestamps = sorted(timestamps_states_issues.keys())

        # словарь(state_id: словарь(timestamp: len(issues_ids))
        states_timestamps_count = {}

        states = []
        for state_id in states_ids:
            if state := gm_holder.project_manager.state(state_id):
                states.append(state)
                states_timestamps_count[state_id] = {}
                for timestamp in ordered_timestamps:
                    states_timestamps_count[state_id][timestamp] = 0
        states = sorted(
            states,
            reverse=True,
            key=lambda state: state.order if state.order else 0
        )

        current_state_issues_ids = {}
        for state_id in states_ids:
            current_state_issues_ids[state_id] = set()

        for timestamp in ordered_timestamps:
            states_issues = timestamps_states_issues[timestamp]

            for state_id, issues_ids in states_issues.items():
                # remove
                for issue_id in issues_ids:
                    for sub_state_id in states_ids:
                        current_state_issues_ids[sub_state_id].discard(issue_id)
                # add
                for issue_id in issues_ids:
                    current_state_issues_ids[state_id].add(issue_id)

            for state_id, issues_ids in current_state_issues_ids.items():
                states_timestamps_count[state_id][timestamp] = len(issues_ids)

        result = []
        for state in states:
            result.append({
                'name': state.caption,
                'data': [ [timestamps, count] for timestamps, count in states_timestamps_count[state.id].items()],
            })

        return json.dumps(result, indent=2)

    #--------------------------------------------------------------------------#
    def current_states_diagram_data(self):
        '''Формирует данные для построения полярной диаграммы с количеством
        запросов в текущем состоянии'''

        states_ids_counts = {}
        for issue in self.issues:
            if not issue.state_id in states_ids_counts:
                states_ids_counts[issue.state_id] = 1
            else:
                states_ids_counts[issue.state_id] += 1

        states_counts = {}
        for state_id, count in states_ids_counts.items():
            if state := gm_holder.project_manager.state(state_id):
                states_counts[state.caption] = count

        return dict(sorted(states_counts.items(), key=lambda item: item[1]))

    #--------------------------------------------------------------------------#
    def lead_time_distribution_diagram_data(self):
        '''Формирует данные для построения спектральной диаграммы распределения
        времени выполнения'''
        finished = {}
        in_progress = {}

        # Временные затраты учитываются только по дочерним задачам самой
        # глубокой вложенности.
        for issue in self.issues:
            days_count = 0
            for sub_issue in gm_holder.project_manager.sub_issues_leaves(issue):
                periods = []
                start_date_time = None

                # Временные промежутки формируются по изменениям состояний.
                for change in sub_issue.ordered_changes():
                    old_state_id, new_state_id = states_from_issue_change(change)
                    if not old_state_id or not new_state_id:
                        continue

                    if not start_date_time:
                        if new_state_id in self.active_states:
                            start_date_time = change.date_time
                    else:
                        periods.append((start_date_time, change.date_time))
                        start_date_time = None

                if start_date_time:
                    periods.append((start_date_time, datetime.datetime.now()))

                for start_date_time, end_date_time in periods:
                    days_count += (end_date_time - start_date_time).days

                # Активность, длительностью меньше одного дня,
                # приравнивается к одному дню.
                if periods and not days_count:
                    days_count = 1

            # Выполнена задача или находится в работе определяется по родителю.
            if days_count:
                container = finished if self.is_finished(issue) else in_progress
                if days_count in container:
                    container[days_count] += 1
                else:
                    container[days_count] = 1

        max_finished = max(finished) if finished else 0
        max_in_progress = max(in_progress) if in_progress else 0
        max_days = max([max_finished, max_in_progress])

        # Формирование результирующих значений для построения диаграммы.
        result_finished = []
        result_in_progress = []
        result_days = []
        for day in range(1, max_days + 1):
            finished_value = finished[day] if day in finished else 0
            in_progress_value = in_progress[day] if day in in_progress else 0
            if in_progress_value or finished_value:
                result_finished.append(finished_value)
                result_in_progress.append(in_progress_value)
                result_days.append(day)

        return (result_finished, result_in_progress, result_days)

    #--------------------------------------------------------------------------#
    def timestamps_states_issues_data(self):
        '''TODO добавить документарий'''

        states_ids = []

        # словарь(timestamp: словарь(state_id: список[issue_id]))
        timestamps_states_issues = {}

        for issue in self.issues:
            created_date_time = issue.created_date_time()
            if not created_date_time:
                continue

            created_timestamp = int(time.mktime(created_date_time.timetuple()) * 1000)
            timestamps_states = {}
            for change in issue.ordered_changes():

                old_state_id, new_state_id = states_from_issue_change(change)
                if not old_state_id or not new_state_id:
                    continue
                
                if not old_state_id in states_ids:
                    states_ids.append(old_state_id)

                if not new_state_id in states_ids:
                    states_ids.append(new_state_id)

                if not created_timestamp in timestamps_states:
                    timestamps_states[created_timestamp] = old_state_id
                
                timestamp = int(time.mktime(change.date_time.timetuple()) * 1000)
                timestamps_states[timestamp] = new_state_id

            for timestamp, state_id in timestamps_states.items():
                if not timestamp in timestamps_states_issues:
                    timestamps_states_issues[timestamp] = {}

                if not state_id in timestamps_states_issues[timestamp]:
                    timestamps_states_issues[timestamp][state_id] = []

                timestamps_states_issues[timestamp][state_id].append(issue.id)

        return (states_ids, timestamps_states_issues)
